package name.jautzy.esi.client.model;

import java.util.LinkedList;
import java.util.List;

import com.google.gwt.i18n.client.NumberFormat;


public class EveFormula {
	private Fit fit;
	public static double maxTime = 600;
	
	public EveFormula(Fit fit) {
		this.fit = fit;
	}
	
	public double timeToDeath(int nb, Turret t) {
		double realDps = realTurretDps(nb, t.getAlpha(), t.getRof(), t.getOpti(), t.getFalloff(), t.getSig(), t.getTrack());
		return timeToDeath(realDps);
	}
	
	public double timeToDeath(int nb, Launcher t) {
		double realDps = realMissileDps(nb, t.getAlpha(), t.getRof(), t.getEv(), t.getEr(), t.getDrf(),t.getVelo());
		return timeToDeath(realDps);
	}
	
	public double realTurretDps(int nb, double alpha, double rof, double opti, double falloff, double tsig, double ttrack) {
		double sig = signature();
		double tvel = velocity();
		double range = opti+falloff/2.0;
		double hitChance = turretHitChance(sig, tvel, range, tsig, ttrack, opti, falloff);
		double dps = 0;
		if (hitChance > 0.01) {
			double c = Math.pow(hitChance, 2)+hitChance+0.0499;
			dps = (alpha/rof)*(c/2.0);
		}
		else
			dps = (alpha/rof)*3*hitChance; 
		return nb*dps;
	}
	
	public double realMissileDps(double nb, double alpha, double rof, double ev, double er, double drf, double mvelo) {
		double sig = signature();
		double vel = velocity();
		
		if (vel>mvelo)
			return 0;
		
		double dps = 0;
		
		double fact = missileFactor(sig,vel, er, ev, drf);
		dps = fact*alpha/rof;
		
		return nb*dps;
	}
	
	double signature() {
		double bsig = fit.getBoostRadiusFactor();
		double boostedSig = fit.getSignature();
		if (bsig!=0)
			boostedSig*=bsig;
		
		return boostedSig;
	}
	
	double velocity() {
		return fit.getBoostedVelocity();
	}
	
	public double timeToDeath(double realDps) {
		double shps = shieldHps();
		if (realDps<shps)
			return maxTime;
		
		double ahps = armorHps();
		if (realDps<ahps)
			return maxTime;
		
		double rech = realDps-shps;
		double st = shieldEffHp()/rech;
		
		rech = realDps-ahps;
		double at = armorEffHp()/rech;
		
		double stt = structEffHp()/realDps;
			
		return Math.min(maxTime, st+at+stt);
	}
	
	public double shieldEffHp() {
		double sr= (fit.getEmSR()+fit.getExSR()+fit.getThSR()+fit.getKiSR())/4.0;
		return fit.getShieldHP()/sr;
	}
	
	public double armorEffHp() {
		double sr= (fit.getEmAR()+fit.getExAR()+fit.getThAR()+fit.getKiAR())/4.0;
		return fit.getArmorHP()/sr;
	}
	
	public double structEffHp() {
		double sr= fit.getStructResonance();
		return fit.getStructHP()/sr;
	}
	
	public double shieldHps() {
		double temp = 2.5*fit.getShieldHP()/fit.getShieldRechTime();
		if (fit.getShieldBoostDuration()!=0)
			temp+=fit.getShieldBoost()/fit.getShieldBoostDuration();
		
		return temp;
	}
	
	public double armorHps() {
		double temp2 = 0;
		if (fit.getArmorRepairDuration()!=0)
			temp2 = fit.getArmorRepair()/fit.getArmorRepairDuration();
		
		return temp2;
	}
	
	public double totalDps(double range) {
		return totalDps(fit.getSignature(), fit.getVelocity(), range);
	}
	
	public double totalDps(TargetShip ship) {
		return totalDps(ship.getSignature(), ship.getVelocity(), bestOptimal());
	}
	
	public double totalDps(double sig, double vel, double range) {
		return missileDps(sig,vel,range)+turretDps(sig,vel,range)+droneDps(sig,vel,range);
	}
	
	
	public double missileDps(double sig, double vel, double range) {
		double alpha = fit.getMissileAlpha();
		if (alpha ==0)
			return 0;
		if (fit.getMissileRof()==0)
			return 0;
		if (range > fit.getTargetRange())
			return 0;
		if (range > missileRange())
			return 0;
		if (vel> fit.getMissileVelocity())
			return 0;
		double fact = missileFactor(sig,vel, fit.getMissileExploRadius(), fit.getMissileExploVelocity(), fit.getMissileDRF());
		return fact*alpha/fit.getMissileRof();
	}
	
	public double turretDps(double sig, double vel, double range) {
		double tvel;
		if (vel==1)
			tvel = vel; // Supposing attacker don't move.
		else tvel = vel-fit.getBoostedVelocity();
		if (tvel < 0)
			tvel = 1;
		double alpha = fit.getTurretAlpha();
		if (alpha ==0)
			return 0;
		if (fit.getTurretRof()==0)
			return 0;
		double hitChance = turretHitChance(sig, tvel, range, fit.getTurretSignature(), fit.getTurretTracking(), fit.getTurretOptimal(), fit.getTurretFalloff());
		double dps = 0;
		if (hitChance > 0.01) {
			double c = Math.pow(hitChance, 2)+hitChance+0.0499;
			dps = (alpha/fit.getTurretRof())*(c/2.0);
		}
		else
			dps = (alpha/fit.getTurretRof())*3*hitChance; 
		return dps;
	}
	
	public double droneDps(double sig, double vel, double range) {
		double tvel = vel; // Supposing attacker don't move.
		double alpha = fit.getDroneAlpha();
		if (alpha ==0)
			return 0;
		if (range > fit.getDroneRange())
			return 0;
		if (fit.getDroneRof()==0)
			return 0;
		double hitChance = turretHitChance(sig, tvel, fit.getDroneOptimal(), fit.getDroneSignature(), fit.getDroneTracking(), fit.getDroneOptimal(), fit.getDroneFalloff());
		double dps = 0;
		if (hitChance > 0.01) {
			double c = Math.pow(hitChance, 2)+hitChance+0.0499;
			dps = (alpha/fit.getDroneRof())*(c/2.0);
		}
		else
			dps = (alpha/fit.getDroneRof())*3*hitChance; 
		return dps;
	}
	
	public double missileRange() {
		return fit.getMissileFlightTime()*fit.getMissileVelocity();
	}
	
	public double turretRange() {
		return fit.getTurretOptimal()+fit.getTurretFalloff()/2.0;
	}
	
	public boolean isLong() {
		return (bestOptimal() > 100000);
	}
	
	public double bestOptimal() {
		if (fit.getMissileAlpha()>fit.getTurretAlpha())
			return Math.min(fit.getTargetRange(),missileRange());
		else
			return Math.min(fit.getTargetRange(),turretRange());
		
	}
	
	public double maxDps() {
		return fit.getMaxDroneDps()+fit.getMaxMissileDps()+fit.getMaxTurretDps();
	}
	
// Helper Methods
	private double missileC(double drf) {
		return 0.586*Math.log(drf);
	}
	
	private double missileFactor(double sig, double vel, double er, double ev, double drf) {
		sig*=fit.getTargetPaintMod();
		vel*=fit.getTargetSpeedMod();
		if (er == 0)
			return 0;
		if (vel==0)
			return Math.min(sig/er, 1.0);
		else if (vel==1)
			return Math.min(1.0,Math.pow((ev/er)*(sig/vel),missileC(drf)));
		else
			return Math.min(Math.min(sig/er, 1.0),
						Math.pow((ev/er)*(sig/vel),missileC(drf)));
	}
	
	private double turretBlob(double sig, double tvel, double range, double tsig, double ttrack, double opti, double falloff) {
		double t1 = tvel*tsig/(ttrack*range*sig);
		double t2 = Math.max(0,range-opti)/falloff;
		return Math.pow(t1,2)+Math.pow(t2,2);
	}
	
	private double turretHitChance (double sig, double tvel, double range, double tsig, double ttrack, double opti, double falloff) 
	{
		sig*=fit.getTargetPaintMod();
		tvel*=fit.getTargetSpeedMod();
		if (range == 0)
			return 0;
		return Math.pow(0.5,turretBlob(sig,tvel,range,tsig,ttrack,opti,falloff));
	}
	
	public static List<String> genEVXAxys(List<Double> values, NumberFormat f) {
		List<String> ret = new LinkedList<String>();
		
		
		for (Double d:values) {
			String s = f.format(d);
			ret.add(s);
		}
			
		
		return ret;
	}
	
// static Public helpers
	public static List<Double> genEVXAxys() {
		List<Double> ret = new LinkedList<Double>();
		ret.add(0.01);ret.add(0.05); //inties
		ret.add(0.1);ret.add(0.15); //logies
		ret.add(0.2);ret.add(0.25); //dictor
		ret.add(0.3);ret.add(0.5); //HAS
		ret.add(0.7);ret.add(1.0);
		ret.add(1.5);ret.add(2.0);
		ret.add(2.5);ret.add(3.0);
		ret.add(3.5);ret.add(4.0);
		return ret;
	}
	
	public static List<Double> genXAxys(double first, double last, int n) {
		List<Double> ret = new LinkedList<Double>();
		
		if (n<2)
			return ret;
		
		double interval = (last-first)/(n-1);
		for (int i=0; i<n;i++) {
			double value = first+i*interval;
			ret.add(value);
		}
			
		
		return ret;
	}

}
